confuse 0.1.0

A fuser-compatible filesystem API facade using Dokan on Windows and fuser elsewhere.
Documentation
# Rust fuser 库 API 架构分析

> 范围说明:本文只基于外部公开信息整理,包括 docs.rs、crates.io、GitHub README 与公开示例源码;未使用任何本地私有代码或非公开资料。版本口径以 docs.rs/crates.io 当前 latest `fuser 0.17.0` 为主,README 中仍出现的旧依赖示例版本仅作为历史/文档现状提示。

## 1. 项目定位

`fuser` 是 Rust 的 FUSE(Filesystem in Userspace)用户态库,描述为 “Filesystem in Userspace (FUSE) for Rust”。它的核心目标不是简单绑定 libfuse,而是以 Rust 方式重写 FUSE low-level userspace library:内核驱动仍由 FUSE 项目提供,开发者实现用户态文件系统逻辑,`fuser` 位于二者之间,负责建立会话、收发内核请求、分派到 Rust trait 方法,并把 reply 写回内核。

官方 README 对运行结构的描述可抽象为三层:

1. **FUSE kernel driver**:注册文件系统并把 VFS 操作转发到用户态进程。
2. **userspace library 层**:传统上由 libfuse 承担;`fuser` 试图用 Rust 替代这层的大部分能力。
3. **用户实现层**:开发者实现具体文件系统行为。

README 还说明:除了挂载/卸载阶段可能依赖 libfuse 外,其余逻辑在 Rust 中运行;在 Linux 上这些 libfuse 调用可通过不启用 `libfuse` feature 移除。crates.io latest `0.17.0` 的默认 feature 为空,`libfuse2`/`libfuse3` 通过 `libfuse` feature 组合提供。

## 2. 总体架构

从公开 API 看,`fuser` 的架构可以按职责分为五层:

```text
应用代码
  └─ 实现 fuser::Filesystem trait
      └─ fuser 分派层:Request + Filesystem 方法 + Reply* 类型
          └─ Session / BackgroundSession / mount2 / spawn_mount2
              └─ FUSE 设备 fd、mount/unmount、内核协议读写
                  └─ FUSE kernel driver / OS VFS
```

### 2.1 应用入口层

开发者通常定义一个文件系统结构体,例如公开 `examples/hello.rs` 中的 `HelloFS`,然后实现 `fuser::Filesystem`。README 的用法也明确写道:创建新文件系统需要实现 `fuser::Filesystem`,并参考 docs.rs 或 `examples`。

### 2.2 trait 分派层

`Filesystem` trait 是 API 核心。docs.rs 显示该 trait 约束为:

```rust
pub trait Filesystem: Send + Sync + 'static { ... }
```

它暴露 41 个默认方法,文档说明这些方法对应 libfuse 的 `fuse_lowlevel_ops`。默认实现可以得到“可挂载但基本不做事”的文件系统,因此使用者可以只覆盖需要支持的操作。

### 2.3 请求上下文层

多数 `Filesystem` 方法第一个参数是 `&Request`。`Request` 是 FUSE request parameters,公开方法包括:

- `unique()`:请求唯一 ID;
- `uid()`:发起请求的用户 ID;
- `gid()`:发起请求的组 ID;
- `pid()`:发起请求的进程 ID。

因此 `Request` 承担的是调用者身份与请求追踪上下文,而不是业务数据载体。业务数据通过 inode、文件句柄、offset、flags、name、path、data slice 等参数传入。

### 2.4 reply 响应层

`fuser` 使用一组一次性 reply 对象表达不同 FUSE 响应类型,例如:

- `ReplyEntry`:目录项查询结果,常用于 `lookup`- `ReplyAttr`:属性结果,常用于 `getattr`/`setattr`- `ReplyData`:字节数据,常用于 `read`/`readlink`- `ReplyDirectory` / `ReplyDirectoryPlus`:目录枚举;
- `ReplyOpen` / `ReplyCreate`:打开/创建文件;
- `ReplyWrite`:写入字节数;
- `ReplyEmpty`:无数据成功或错误;
- `ReplyStatfs``ReplyXattr``ReplyLock``ReplyIoctl``ReplyPoll``ReplyBmap``ReplyLseek` 等。

这种设计把“操作处理”和“响应发送”显式绑定:每个回调拿到对应的 reply 类型,然后调用成功方法或 `error(...)`。例如 `ReplyEntry::entry(ttl, attr, generation)` 返回目录项,`ReplyAttr::attr(ttl, attr)` 返回属性,`ReplyDirectory::add(...)` 逐项填充目录缓冲区并用 `ok()` 完成,`ReplyOpen::opened(fh, flags)` 返回文件句柄和打开标志。

## 3. 核心数据模型

### 3.1 inode 与文件句柄

`fuser` 使用 newtype 表示核心标识:

- `INodeNo`:inode number;
- `FileHandle`:文件句柄;
- `Generation`:generation number;
- `RequestId`:内核请求唯一 ID;
- `LockOwner`:锁拥有者。

这比裸 `u64` 更贴近 Rust 类型接口,也能减少参数位置误用。公开 `hello.rs` 示例用 `INodeNo::ROOT` 表示根目录,用 `INodeNo(2)` 表示 `hello.txt`。

### 3.2 FileAttr

`FileAttr` 是文件属性结构,字段包括:

- 标识与大小:`ino``size``blocks`- 时间:`atime``mtime``ctime``crtime`- 类型和权限:`kind: FileType``perm``nlink`- 所有者:`uid``gid`- 设备/块信息:`rdev``blksize`- 平台 flags:`flags`,文档标注 macOS only。

`FileType` 表达目录、普通文件等类型。公开示例中,目录属性使用 `FileType::Directory`、权限 `0o755`、链接数 2;普通文件使用 `FileType::RegularFile`、权限 `0o644`。

### 3.3 错误模型

`Errno` 表示返回给调用方的错误码。公开示例中,未找到 inode 或 name 时调用 `reply.error(Errno::ENOENT)`。这说明 API 的错误表达贴近 POSIX/FUSE:回调本身一般不返回 `Result`,而是通过 reply 对象发送成功或 errno。

## 4. Filesystem trait 的操作分组

docs.rs 列出的 41 个方法可按文件系统语义分组:

### 4.1 生命周期与协商

- `init(&mut self, req, config) -> Result<()>`:第一个被调用的方法,可通过 `KernelConfig` 配置内核连接;
- `destroy(&mut self)`:文件系统退出时清理。

### 4.2 名称解析与 inode 生命周期

- `lookup(parent, name, ReplyEntry)`:按父目录 inode 和名称查找目录项;
- `forget(ino, nlookup)`:内核忘记 inode 引用;
- `batch_forget(nodes)`:批量 forget,默认回退到逐个 `forget`
文档建议:如果文件系统实现 inode 生命周期,每次 lookup 获取一个引用,forget 时减少 `nlookup` 个引用;卸载时不保证所有引用都会收到 forget。

### 4.3 属性与元数据

- `getattr(ino, fh, ReplyAttr)`- `setattr(...)`- `statfs(ino, ReplyStatfs)`- `access(ino, mask, ReplyEmpty)`
`access` 文档指出,如果使用 `default_permissions` 挂载选项,则该方法不会被调用,因为权限检查交由内核执行。

### 4.4 目录项与链接修改

- `mknod``mkdir`- `unlink``rmdir`- `symlink``readlink`- `rename``link`
这些方法围绕父 inode、名称、目标路径、flags 和 `ReplyEntry`/`ReplyEmpty` 建模。

### 4.5 文件 I/O

- `open(ino, flags, ReplyOpen)`- `read(ino, fh, offset, size, flags, lock_owner, ReplyData)`- `write(ino, fh, offset, data, write_flags, flags, lock_owner, ReplyWrite)`- `flush``release``fsync`- `create(...)`:创建并打开文件;
- `fallocate``lseek``copy_file_range`
`open` 文档说明文件系统可以把任意文件句柄(指针、索引等)保存在 `fh` 中,后续 `read/write/flush/release/fsync` 使用。也可以实现无状态 I/O,不保存句柄。

`read` 文档强调:除 EOF 或错误外,应返回请求的精确字节数,否则剩余数据可能被零填充;`direct_io` 模式例外。`write` 类似:除错误外应返回请求写入字节数。

### 4.6 目录 I/O

- `opendir`- `readdir`- `readdirplus`- `releasedir`- `fsyncdir`
`ReplyDirectory::add(ino, offset, kind, name)` 返回 `bool` 表示缓冲区是否已满;调用者需要在满时停止追加,并最终 `ok()`。公开 `hello.rs` 示例使用 `offset` 跳过已读条目,并把 `i + 1` 作为下一次读取的 offset。

### 4.7 扩展属性、锁与特殊操作

- xattr:`setxattr``getxattr``listxattr``removexattr`- 锁:`getlk``setlk`- 其他:`bmap``ioctl``poll`
这些 API 说明 `fuser` 不只覆盖最小文件读写,还试图覆盖 libfuse low-level interface 的大量系统级能力。

## 5. 会话与挂载 API

### 5.1 Config 与 MountOption

`Config` 是 session 配置结构,标记为 `#[non_exhaustive]`,字段包括:

- `mount_options: Vec<MountOption>`:挂载选项;
- `acl: SessionACL`:谁能访问文件系统;
- `n_threads: Option<usize>`:事件循环线程数,未指定时为单线程;
- `clone_fd: bool`:使用 `FUSE_DEV_IOC_CLONE` 给每个 worker thread 独立 fd,Linux 4.5+ 可提升多线程请求处理效率。

`MountOption` 包括 `FSName`、`Subtype`、`CUSTOM`、`AutoUnmount`、`DefaultPermissions`、`RO/RW`、`Exec/NoExec`、`Atime/NoAtime`、`Sync/Async` 等。`CUSTOM(String)` 为未枚举的 mount option 留出口。

### 5.2 前台挂载:mount2

`mount2(filesystem, mountpoint, options) -> Result<()>` 挂载文件系统并阻塞,直到文件系统卸载。文档说明如果选项错误、FUSE device 无法挂载,或 session 结束时有最终错误,会返回 error。

### 5.3 后台挂载:spawn_mount2

`spawn_mount2(filesystem, mountpoint, options) -> Result<BackgroundSession>` 挂载后启动后台线程处理文件系统操作,并立即返回。返回的 `BackgroundSession` 必须被保存;如果被 drop,文件系统会卸载。

### 5.4 Session 低层控制

`Session<FS>` 是会话数据结构,提供:

- `Session::new(filesystem, mountpoint, options)`:创建并挂载;
- `Session::from_fd(filesystem, fd, acl, config)`:包装已有 `/dev/fuse` fd,本身不执行挂载;
- `spawn()`:后台运行 session loop;
- `unmount()`:卸载;
- `unmount_callable()`:返回线程安全卸载对象;
- `notifier()`:返回可向 kernel 发送通知的对象。

因此 API 同时提供简化入口(`mount2`/`spawn_mount2`)和较底层的 session 控制能力。

## 6. 典型调用链

基于 docs.rs 与公开 `hello.rs` 示例,可以把一次 `cat hello.txt` 的路径概括为:

1. 应用构造文件系统结构体并调用 mount API。
2. 内核收到路径访问后向 FUSE 发送 lookup 请求。
3. `fuser` 解析请求,调用 `Filesystem::lookup(&self, &Request, parent, name, ReplyEntry)`4. 实现方根据 `(parent, name)` 查表,调用 `reply.entry(&ttl, &FileAttr, Generation(...))``reply.error(Errno::ENOENT)`5. 内核随后请求属性或打开文件,触发 `getattr` / `open`6. 读取时触发 `read(ino, fh, offset, size, ..., ReplyData)`7. 实现方按 offset 切片并调用 `reply.data(bytes)`,错误时调用 `reply.error(errno)`
目录列表则是 `readdir`:实现方用 `ReplyDirectory::add` 填充 `.`、`..` 与子项;如果 buffer 满则停止,并调用 `ok()`。

## 7. 并发与状态管理含义

`Filesystem: Send + Sync + 'static` 暗示 `fuser` 可以在线程间共享文件系统对象。`Config::n_threads` 与 `clone_fd` 进一步说明 session loop 支持多线程处理。公开 `hello.rs` 示例为了统计每线程 read 次数,使用 `AtomicU64`、`AtomicUsize` 和 thread-local `THREAD_INDEX`,也从用法侧印证:实现者需要自行保证内部状态的并发安全。

因此架构上,`fuser` 管分派和协议,业务对象的缓存、inode 表、句柄表、锁、目录快照一致性等由实现方负责。常见实现会需要:

- inode -> 元数据/内容的索引;
- path/name -> inode 的目录项索引;
- open file handle -> 后端资源或游标;
- TTL 策略,控制内核缓存属性和目录项的时间;
- 并发控制,通常用锁、原子变量或内部并发数据结构。

公开 `examples/simple.rs` 展示了比 hello 更完整的状态管理形态:维护 inode 分配、目录/文件元数据、文件句柄、目录句柄、访问检查、扩展属性和创建/修改操作。这类示例说明,`fuser` 并不替应用持有完整 VFS 状态;它提供协议分派与类型化回包,状态模型仍由具体文件系统设计。

## 8. 平台与构建约束

README 说明:

- Linux:通常安装 `fuse``fuse3`,该 crate 兼容两者;构建需要 FUSE headers/libraries 与 `pkg-config`- macOS:README 标注 macOS 支持为 “untested”,依赖 macFUSE;Apple Silicon 需要启用第三方 kernel extensions。
- FreeBSD:安装 `fusefs-libs``pkgconf`- README 的兼容性部分称项目在 Linux 开发和测试,并在 Linux 与 FreeBSD 上用 stable Rust 测试。

crates.io `0.17.0` 元数据显示:license 为 MIT,edition 为 2024,Rust version 为 1.85,latest/max stable version 为 0.17.0。

## 9. Feature flags 与演进面

`0.17.0` 公开 feature flags 主要包括:

- ABI 相关:`abi-7-20``abi-7-40`- `libfuse``libfuse2``libfuse3`- `macfuse-4-compat``macos-no-mount`- `serializable` -> `serde`- `experimental` -> `async-trait` + `tokio`- `default` 不启用额外 feature。

README 提到项目源自 `fuse` crate fork,目标之一是继续开发并增加 ABI 7.19 之后的特性;当前 feature flags 也体现了它围绕 FUSE ABI 版本演进的设计。

## 10. API 设计特点

### 10.1 低层 FUSE 映射,而非高级虚拟文件系统框架

`Filesystem` 方法与 `fuse_lowlevel_ops` 对齐,参数直接暴露 inode、fh、offset、flags、errno、xattr、lock 等概念。这给实现者较高控制力,但也要求理解 FUSE/VFS 语义。

### 10.2 类型化 reply 约束响应形态

每个操作拿到专用 reply 类型,成功路径方法与该操作语义匹配,错误统一通过 errno。这样避免了用一个泛型返回值承载所有操作响应,也更接近内核协议的单次请求/单次响应模型。

### 10.3 默认方法降低最小实现成本

所有主要操作都有默认实现;最小文件系统只需覆盖必要方法。公开 hello 示例只覆盖 `lookup`、`getattr`、`read`、`readdir` 即可提供只读目录与文件。

### 10.4 缓存由 TTL 和 mount option 共同影响

`ReplyEntry::entry`、`ReplyAttr::attr` 都接收 TTL;示例中动态文件 `stats-per-thread` 使用 `Duration::ZERO`,注释说明否则旧 size 会被缓存。这表明实现者必须显式设计缓存语义。

### 10.5 session API 分层清晰

简单使用者调用 `mount2` 或 `spawn_mount2`;高级使用者可直接操作 `Session`、已有 fd、notifier、unmounter。这种分层兼顾易用入口与系统级控制。

## 11. 实现一个 fuser 文件系统的最小结构

公开资料可归纳出典型 skeleton:

```rust
struct MyFs {
    // inode table, file data, handle table, etc.
}

impl fuser::Filesystem for MyFs {
    fn lookup(&self, req: &fuser::Request, parent: fuser::INodeNo, name: &std::ffi::OsStr, reply: fuser::ReplyEntry) {
        // find child under parent, then reply.entry(...) or reply.error(...)
    }

    fn getattr(&self, req: &fuser::Request, ino: fuser::INodeNo, fh: Option<fuser::FileHandle>, reply: fuser::ReplyAttr) {
        // return FileAttr or errno
    }

    fn readdir(&self, req: &fuser::Request, ino: fuser::INodeNo, fh: fuser::FileHandle, offset: u64, reply: fuser::ReplyDirectory) {
        // fill directory entries and call ok()
    }

    fn read(&self, req: &fuser::Request, ino: fuser::INodeNo, fh: fuser::FileHandle, offset: u64, size: u32, flags: fuser::OpenFlags, lock_owner: Option<fuser::LockOwner>, reply: fuser::ReplyData) {
        // return bytes or errno
    }
}
```

挂载侧则使用 `Config` 与 mount option:

```rust
let mut cfg = fuser::Config::default();
cfg.mount_options.push(fuser::MountOption::RO);
cfg.mount_options.push(fuser::MountOption::FSName("myfs".to_string()));
fuser::mount2(MyFs { /* ... */ }, mountpoint, &cfg)?;
```

实际项目需要根据是否阻塞主线程选择 `mount2` 或 `spawn_mount2`,并根据并发模型设置 `n_threads` 与内部同步结构。

## 12. 适用场景与注意点

适合:

- 用 Rust 快速实现用户态文件系统;
- 原型化只读/虚拟/聚合文件系统;
- 对接对象存储、数据库、远程 API 或内存数据结构为文件系统视图;
- 需要较低层 FUSE 控制能力的系统工具。

注意:

- API 贴近 low-level FUSE,必须正确处理 inode 生命周期、offset、TTL、errno、权限和并发;
- 默认实现“可挂载但不做事”,不等于提供 POSIX 完整语义;
- `DefaultPermissions` 会改变权限检查责任边界;
- 多线程 session 下,`Filesystem` 内部状态必须线程安全;
- 动态属性如果 TTL 过长,会被内核缓存导致 size/metadata 过期;
- macOS 支持在 README 中标注为 untested,平台差异需要单独验证。

## 13. 公开来源

- docs.rs crate page: https://docs.rs/fuser/latest/fuser/
- docs.rs `Filesystem` trait: https://docs.rs/fuser/latest/fuser/trait.Filesystem.html
- docs.rs `FileAttr`: https://docs.rs/fuser/latest/fuser/struct.FileAttr.html
- docs.rs `Request`: https://docs.rs/fuser/latest/fuser/struct.Request.html
- docs.rs `Config`: https://docs.rs/fuser/latest/fuser/struct.Config.html
- docs.rs `MountOption`: https://docs.rs/fuser/latest/fuser/enum.MountOption.html
- docs.rs `mount2`: https://docs.rs/fuser/latest/fuser/fn.mount2.html
- docs.rs `spawn_mount2`: https://docs.rs/fuser/latest/fuser/fn.spawn_mount2.html
- docs.rs `Session`: https://docs.rs/fuser/latest/fuser/struct.Session.html
- docs.rs feature flags: https://docs.rs/crate/fuser/latest/features
- crates.io API metadata: https://crates.io/api/v1/crates/fuser
- GitHub README: https://github.com/cberner/fuser/blob/master/README.md
- GitHub hello example: https://raw.githubusercontent.com/cberner/fuser/master/examples/hello.rs
- GitHub simple example: https://raw.githubusercontent.com/cberner/fuser/master/examples/simple.rs